#include "mysql.h"

sql::Connection *Mysql::conn_ = nullptr;
sql::Statement *Mysql::stmt_ = nullptr;
sql::ResultSet *Mysql::rs_ = nullptr;
Mysql *Mysql::mysql_ = new Mysql();

bool Mysql::deleteFile(int uid, int code, string fname)
{
    shared_ptr<VirtualDirec> dir = queryFile(uid, code, fname);
    if (!dir)
    {
        return false;
    }
    stmt_ = conn_->createStatement();
    string sql;
    if (dir->md5sum.empty())
    {
        // 要删除的是文件夹
        sql += "select * from virtual_direc where uid = ";
        sql += std::to_string(uid);
        sql += " and precode = ";
        sql += std::to_string(dir->code);

#ifdef DEBUG
        cout << "Mysql deleteFile: " << sql << endl;
#endif

        rs_ = stmt_->executeQuery(sql);
        if (rs_->next())
        {
            return false;
        }

        free();
        deleteFile_(uid, code, fname);
    }
    else
    {
        // 删除的是文件
        free();
        deleteFile_(uid, code, fname);
        unlink(dir->md5sum);
    }
    free();
    return true;
}

void Mysql::deleteFile_(int uid, int code, string fname)
{
    stmt_ = conn_->createStatement();
    string sql;
    sql = "delete from virtual_direc where uid = ";
    sql += std::to_string(uid);
    sql += " and precode = ";
    sql += std::to_string(code);
    sql += " and fname = '";
    sql += fname;
    sql += "'";

#ifdef DEBUG
    cout << "Mysql deleteFile_: " << sql << endl;
#endif

    stmt_->execute(sql);
    free();
}

void Mysql::unlink(string md5sum)
{
    stmt_ = conn_->createStatement();
    string sql = "update file set linked = linked - 1 where md5sum = '";
    sql += md5sum;
    sql += "'";

#ifdef DEBUG
    cout << "Mysql unlink: " << sql << endl;
#endif

    stmt_->executeUpdate(sql);

    sql.clear();
    sql = "delete from file where linked = 0 and md5sum = '";
    sql += md5sum;
    sql += "'";

#ifdef DEBUG
    cout << "Mysql unlink: " << sql << endl;
#endif

    stmt_->execute(sql);
    free();
}

shared_ptr<VirtualDirec> Mysql::queryFile(int uid, int code, string path)
{
    shared_ptr<VirtualDirec> dir = nullptr;
    stmt_ = conn_->createStatement();
    string sql = "select * from virtual_direc where uid = ";
    sql += std::to_string(uid);
    sql += " and precode = ";
    sql += std::to_string(code);
    sql += " and fname = '";
    sql += path;
    sql += "'";

#ifdef DEBUG
    cout << "Mysql queryFile: " << sql << endl;
#endif
    rs_ = stmt_->executeQuery(sql);

    if (rs_->next())
    {
        dir = std::make_shared<VirtualDirec>();
        dir->id = rs_->getInt(1);
        dir->uid = rs_->getInt(2);
        dir->code = rs_->getInt(3);
        dir->precode = rs_->getInt(4);
        dir->fname = rs_->getString(5);
        dir->md5sum = rs_->getString(6);
    }

    free();
    return dir;
}

void Mysql::updateFile(int fid, int linked)
{
    stmt_ = conn_->createStatement();
    string sql = "update file set linked = ";
    sql += std::to_string(linked);
    sql += " where fid = ";
    sql += std::to_string(fid);

#ifdef DEBUG
    cout << "Mysql updateFile: " << sql << endl;
#endif

    stmt_->executeUpdate(sql);

    free();
}

shared_ptr<File> Mysql::queryFile(const string &md5)
{
    shared_ptr<File> file = nullptr;
    stmt_ = conn_->createStatement();
    string sql = "select * from file where md5sum = '";
    sql += md5;
    sql += "'";

#ifdef DEBUG
    cout << "Mysql updateFile: " << sql << endl;
#endif

    rs_ = stmt_->executeQuery(sql);

    if (rs_->next())
    {
        file = std::make_shared<File>();

        file->fid = rs_->getInt(1);
        file->fname = rs_->getString(2);
        file->md5sum = rs_->getString(3);
        file->linked = rs_->getInt(4);
    }
    free();

    return file;
}

void Mysql::insertFile(const string &fname, const string &md5)
{
    stmt_ = conn_->createStatement();
    string sql = "insert into file(fname, md5sum, linked) values('";
    sql += fname;
    sql += "', '";
    sql += md5;
    sql += "', ";
    sql += "1)";

#ifdef DEBUG
    cout << "Mysql updateFile: " << sql << endl;
#endif

    stmt_->execute(sql);

    free();
}

void Mysql::insertDir(int uid, int code, const string &path)
{
    shared_ptr<User> user = queryUserByUid(uid);
    stmt_ = conn_->createStatement();

    string sql = "insert into virtual_direc(uid, code, precode, fname) values(";
    sql += std::to_string(uid);
    sql += ", ";
    sql += std::to_string(code + 1);
    sql += ", ";
    sql += std::to_string(code);
    sql += ", '";
    sql += path;
    sql += "')";

#ifdef DEBUG
    cout << "Mysql insertPath: " << sql << endl;
#endif

    int effRow = stmt_->executeUpdate(sql);

    free();
}

void Mysql::insertFile(int uid, int code, const string &path, const string &md5sum)
{
    shared_ptr<User> user = queryUserByUid(uid);
    stmt_ = conn_->createStatement();

    string sql = "insert into virtual_direc(uid, code, precode, fname, md5sum) values(";
    sql += std::to_string(uid);
    sql += ", ";
    sql += std::to_string(code + 1);
    sql += ", ";
    sql += std::to_string(code);
    sql += ", '";
    sql += path;
    sql += "', '";
    sql += md5sum;
    sql += "')";

#ifdef DEBUG
    cout << "Mysql insertPath: " << sql << endl;
#endif

    int effRow = stmt_->executeUpdate(sql);
    free();
}

shared_ptr<User> Mysql::queryUserByUid(int uid)
{

    shared_ptr<User> user = nullptr;
    stmt_ = conn_->createStatement();

    string sql = "select * from user where uid = ";
    sql += std::to_string(uid);

    rs_ = stmt_->executeQuery(sql);

    if (rs_->next())
    {
        user = std::make_shared<User>();

        user->uid = rs_->getInt(1);
        user->uname = rs_->getInt(2);
        user->upwd = rs_->getInt(3);
        user->encrypt = rs_->getString(4);
        user->root_path = rs_->getString(5);
    }

#ifdef DEBUG
    cout << "Mysql queryUserById DEBUG..." << endl;
#endif

    free();
    return user;
}

shared_ptr<User> Mysql::queryUserByUnamePwd(string uname, string pwd)
{

    shared_ptr<User> user = nullptr;
    stmt_ = conn_->createStatement();

    string sql = "select * from user where uname = '";
    sql += uname += "' and ";
    sql += "upwd = ";
    sql += pwd;

#ifdef DEBUG
    cout << "queryUserByUnamePwd: " << sql << endl;
#endif

    rs_ = stmt_->executeQuery(sql);

    if (rs_->next())
    {
        user = std::make_shared<User>();

        user->uid = rs_->getInt(1);
        user->uname = rs_->getInt(2);
        user->upwd = rs_->getInt(3);
        user->encrypt = rs_->getString(4);
        user->root_path = rs_->getString(5);
    }

#ifdef DEBUG
    cout << "Mysql queryUserById DEBUG..." << endl;
#endif

    free();
    return user;
}

shared_ptr<User> Mysql::insert(string uname, string upwd)
{
    shared_ptr<User> user = nullptr;
    stmt_ = conn_->createStatement();
    string path = Path::getRootPath(uname);
    string sql = "insert into user(uname, upwd, root_path) values('";
    sql += uname;
    sql += "', '";
    sql += upwd;
    sql += "', '";
    sql += path;
    sql += "')";

#ifdef DEBUG
    cout << "Mysql insert: " << sql << endl;
#endif

    try
    {
        int ret = stmt_->executeUpdate(sql);
        free();
        user = queryUserByUnamePwd(uname, upwd);
        ret = mkdir(path.c_str(), 0766);
        if (ret == -1)
        {
            perror("Mysql::insert mkdir");
            free();
            deleteUserByUid(user->uid);

            return nullptr;
        }

        return user;
    }
    catch (sql::SQLException &e)
    {
        cout << e.what() << endl;
        ;
        free();
        return user;
    }
}

// Todo: 完善多个用户同时link同一个文件
void Mysql::deleteUserByUid(int uid)
{
    stmt_ = conn_->createStatement();
    string sql = "delete from user where uid = ";
    sql += uid;

    int ret = stmt_->executeUpdate(sql);
    free();
}

void Mysql::free()
{
    if (rs_)
        delete rs_;
    if (stmt_)
        delete stmt_;
    rs_ = nullptr;
    stmt_ = nullptr;
}

Mysql::Mysql()
{
    sql::mysql::MySQL_Driver *driver =
        sql::mysql::get_mysql_driver_instance();
    conn_ = driver->connect(MYSQL_HOST_, MYSQL_USER_, MYSQL_PWD_);
    conn_->setSchema(MYSQL_DB_);
}
Mysql::~Mysql()
{
    delete conn_;
}